package kevin.framework.basic.common.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.web.context.ContextLoaderListener;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import kevin.framework.basic.common.thread.DaoDBKeyHolder;
import kevin.framework.basic.common.thread.OperatorDataHolder;
import kevin.framework.basic.common.thread.RequestThreadHolder;
import kevin.framework.basic.common.thread.UrlInfoThreadHodler;
import kevin.framework.basic.common.ui.LanguageMap;
import kevin.framework.basic.common.ui.LanguageStore;
import net.sf.ehcache.CacheManager;

/**
 * @author kevin.huang
 * @ClassName: WebContextLoaderListener
 * @Description: TODO
 * @date 2016年4月23日 下午1:45:32
 */
public class WebContextLoaderListener extends ContextLoaderListener {

    @Override
    public void contextDestroyed(final ServletContextEvent event) {
        final WebContextLoaderListener seft = this;
        SpringContextUtil.onContextDestroyed(new IBeforeContextDestroyed() {
            @Override
            public void destroyed() {
                seft.callSuperDestory(event);
            }
        });
    }

    public void callSuperDestory(ServletContextEvent event) {
        CacheManager.getInstance().shutdown();
        DaoDBKeyHolder.clear();// 清除该线程可能绑定了的dbkey
        OperatorDataHolder.clear();
        UrlInfoThreadHodler.clear();
        RequestThreadHolder.clear();
        super.contextDestroyed(event);
        System.out.println("web context closed ok！");
    }

    /*
     * 外置配置文件管理 可以将common.properties、log4j.properties、except-validation.xml、jdbc.
     * properties放置任意系统目录，同时 配置名为“KEVIN_WEB_CONFIG_HOME”的jvm或者系统的环境变量指向该目录即可
     */
    @Override
    public void contextInitialized(ServletContextEvent event) {
        Locale locale2 = Locale.getDefault();
        java.util.Locale.setDefault(locale2);
        PathMatchingResourcePatternResolver resoler = new PathMatchingResourcePatternResolver();
        Properties prop = new Properties();
        String defaultClassPath = "classpath:/configHome"; // 默认class path 根路径
        Logger logger = null;
        InputStream commoninStream = null;
        InputStream validationStream = null;
        InputStream jdbcInputStream = null;
        String configHome;
        String configHomeEnvName = "KEVIN_WEB_CONFIG_HOME";
        String message = "";
        try {
            // 优先获取jvm启动参数
            configHome = System.getProperty(configHomeEnvName);

            // 获取系统环境变量
            if (StringUtils.isEmpty(configHome)) {
                Map<String, String> map = System.getenv();
                configHome = map.get(configHomeEnvName);
                message = "CONFIGURATION USE SYSTEM ARGS ";
            } else {
                message = "CONFIGURATION USE JVM ARGS ";
            }
            if (StringUtils.isNotEmpty(configHome)) {
                SysConfigParams.isDefaultClassPath = false;
                if (OSinfo.isWindows()) {
                    SysConfigParams.configHome = "file:/" + configHome;
                } else {
                    SysConfigParams.configHome = "file:" + configHome;
                }
            } else {
                message = "CONFIGURATION USE DEFAULT CLASS PATH ";
                SysConfigParams.isDefaultClassPath = true;
                SysConfigParams.configHome = defaultClassPath;
            }

            // 重新初始化Log4j2的配置上下文 log4j2 配置文件加载
            LoggerContext context = (LoggerContext)LogManager.getContext(false);
            String logFile = SysConfigParams.configHome + File.separator + "log4j2.xml";
            Resource logResource = resoler.getResource(logFile);
            if (!logResource.exists()) {
                String clazzLogFile = defaultClassPath + File.separator + "log4j2.xml";
                logResource = resoler.getResource(clazzLogFile);
                System.out.println(logFile + " is not found ,apply " + clazzLogFile);
            }
            if (logResource.exists()) {
                context.setConfigLocation(logResource.getURI());
                context.reconfigure();
                logger = LoggerFactory.getLogger(WebContextLoaderListener.class);
            }

            // 通用key-value配置文件加载
            String commonfile = SysConfigParams.configHome + File.separator + "common.properties";
            Resource commonfileResource = resoler.getResource(commonfile);
            if (commonfileResource.exists()) {
                commoninStream = commonfileResource.getInputStream();
            } else {
                String tmp = defaultClassPath + File.separator + "common.properties";
                commonfileResource = resoler.getResource(tmp);
                if (commonfileResource.exists()) {
                    commoninStream = commonfileResource.getInputStream();
                    System.out.println("the " + commonfile + " is not found ,apply file " + tmp);
                }
            }
            if (commoninStream != null) {
                prop.load(commoninStream);
                String devModel = prop.getProperty("devModel").trim();
                SysConfigParams.isDevModel = "true".equals(devModel) ? true : false;
                SysConfigParams.desKey = prop.getProperty("desKey").trim();
                SysConfigParams.jdbcFile = prop.getProperty("jdbcFile").trim();
                SysConfigParams.cookieKey = DigestUtils.md5Hex(prop.getProperty("cookieKey").trim());
                for (Object propKey : prop.keySet()) {
                    SysConfigParams.setConfig(String.valueOf(propKey), String.valueOf(prop.get(propKey)));
                }
            }

            // 权限特殊验证配置加载
            String validFile = SysConfigParams.configHome + File.separator + "except-validation.xml";
            Resource validFileResource = resoler.getResource(validFile);
            if (validFileResource.exists()) {
                validationStream = validFileResource.getInputStream();
            } else {
                String tmp = defaultClassPath + File.separator + "except-validation.xml";
                if (validFileResource.exists()) {
                    validationStream = validFileResource.getInputStream();
                    System.out.println("the " + validFile + " is not found ,apply file " + tmp);
                }
            }
            if (validationStream != null) {
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document doc = builder.parse(validationStream);
                NodeList list = doc.getElementsByTagName("controller");
                for (int i = 0; i < list.getLength(); i++) {
                    Element node = (Element)list.item(i);
                    String c = node.getAttribute("name");
                    String access = node.getAttribute("access");
                    if (StringUtils.isNotEmpty(access)) {
                        SysConfigParams.setAccess(c, "*");
                    }
                    NodeList cmdlist = node.getElementsByTagName("cmd");
                    if (cmdlist.getLength() > 0) {
                        for (int k = 0, len = cmdlist.getLength(); k < len; ++k) {
                            Element n = (Element)cmdlist.item(k);
                            String cmd = n.getFirstChild().getNodeValue();
                            String cmdAccess = node.getAttribute("access");
                            if (StringUtils.isNotEmpty(cmdAccess)) {
                                SysConfigParams.setAccess(c, cmd);
                            }
                            SysConfigParams.setExceptValidation(c, cmd);
                        }
                    }
                }
            }
            // jdbc 配置文件加载
            if (logger.isDebugEnabled()) {
                logger.debug("JDBC FILE =" + SysConfigParams.configHome + File.separator + SysConfigParams.jdbcFile);
            }

            String jdbcFile = SysConfigParams.configHome + File.separator + SysConfigParams.jdbcFile;
            Resource JdbcResouce = resoler.getResource(jdbcFile);
            if (JdbcResouce.exists()) {
                jdbcInputStream = JdbcResouce.getInputStream();
            } else {
                String tmp = defaultClassPath + File.separator + SysConfigParams.jdbcFile;
                JdbcResouce = resoler.getResource(tmp);
                if (JdbcResouce.exists()) {
                    jdbcInputStream = JdbcResouce.getInputStream();
                    System.out.println("the " + jdbcFile + " is not found ,apply file " + tmp);
                }
            }
            if (jdbcInputStream != null) {
                SysConfigParams.JdbcResouce = JdbcResouce;
                prop.load(jdbcInputStream);
                for (Entry<Object, Object> entry : prop.entrySet()) {
                    String k = String.valueOf(entry.getKey());
                    String val = String.valueOf(entry.getValue());
                    SysConfigParams.setConfig(k, val);
                }
            }
            if (logger.isDebugEnabled()) {
                logger.debug("System ContextLoaderListener Loaded config ok!");
                System.out.println("***************************" + message + "***************************");
            }
        } catch (Exception e) {
            if (logger != null) {
                logger.error("System ContextLoaderListener Loaded config fail:" + e);
            } else {
                System.out.println(e);
            }
        } finally {
            if (jdbcInputStream != null) {
                try {
                    jdbcInputStream.close();
                } catch (Exception e) {

                }
            }
            if (validationStream != null) {
                try {
                    validationStream.close();
                } catch (Exception e) {

                }
            }
            if (commoninStream != null) {
                try {
                    commoninStream.close();
                } catch (Exception e) {

                }
            }
        }
        System.setProperty(CacheManager.ENABLE_SHUTDOWN_HOOK_PROPERTY, "true");

        try {
            loadBasicLangConfig(logger);
        } catch (IOException e) {
            e.printStackTrace();
        }
        ServletContext ctx = event.getServletContext();
        SysConfigParams.webHttpContext = ctx.getContextPath();

        super.contextInitialized(event);

        System.out.println("web contextInitialized ok!");

    }

    /******
     * BasicController.properties 所有controller共有的，采用静态共享一次性加载
     * 
     * @throws IOException
     *****/
    private void loadBasicLangConfig(Logger logger) throws IOException {
        Map<String, LanguageMap> languageMap = LanguageStore.getLanguageStore();
        String langRoot;
        if (SysConfigParams.isDefaultClassPath) {
            langRoot = new File(this.getClass().getResource("/").getPath() + "configHome/langs/").getAbsolutePath();
        } else {
            langRoot = SysConfigParams.configHome + "/langs/";
        }
        File dirFile = new File(langRoot);
        if (!dirFile.exists()) {
            return;
        }
        String[] fileNames = dirFile.list();

        for (String fName : fileNames) {
            logger.debug("load language [" + fName + "]");
            LanguageMap map = new LanguageMap();
            languageMap.put(fName, map);
            String fPath = langRoot + File.separatorChar + fName;
            File file = new File(fPath);
            if (file.exists()) {
                File[] fileArray = file.listFiles();
                for (File f : fileArray) {
                    Properties properties = new Properties();
                    FileInputStream in = new FileInputStream(f);
                    properties.load(in);
                    in.close();
                    for (Object key : properties.keySet()) {
                        String k = String.valueOf(key);
                        String v = properties.getProperty(k);
                        map.setLangKeyValue(k, v);
                    }
                }
            }
        }

    }

}
